package com.apobates.forum.toro.proxy;

import com.apobates.forum.toro.core.BeanParser;
import com.apobates.forum.toro.core.Corner;
import com.apobates.forum.toro.core.Express;
import com.apobates.forum.toro.core.TypeEnum;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
/**
 * 聚合属性语句
 * 适用于: 一对一 , 多对一
 *
 * @author xiaofanku@live.cn
 * @since 20220517
 */
public class MapperAttrStatements {
    /**
     * 查询表达式集合
     */
    private Set<MapperAttrExpression> selectSet;
    private final static BeanParser<MapperAttrStatements.Argument> parser;
    static {
        // targetAttr:collectionType=select(selectAttr:selectAttrTypeName)
        Express exp = Express.getInstance()
                .word("targetAttr")
                .corner(Corner.sequence(0).edge(":").build())
                .word("collectionType")
                .corner(Corner.sequence(1).require(false).give("bean").edge("=select(").build())
                .word("selectAttr")
                .corner(Corner.sequence(2).edge(":").build())
                .word("selectAttrTypeName")
                .corner(Corner.sequence(3).require(false).give("long").edge(")").build());
        // 从表达式到实例
        parser = exp.build(MapperAttrStatements.Argument.class);
    }

    /**
     * MapperAttrStatements构造函数
     */
    public MapperAttrStatements() {
        this.selectSet = new HashSet<>();
    }

    /**
     * MapperAttrStatements构造函数
     * @param selectSet
     */
    private MapperAttrStatements(Set<MapperAttrExpression> selectSet) {
        this.selectSet = selectSet;
    }

    /**
     * 设置目标对象的属性名称.
     * 通过Argument构造者设置关联属性。注意：不要与expr混用
     *
     * @param targetAttr 目标对象的属性名称
     * @return 属性表达式语句构建器
     */
    public Argument attr(String targetAttr) {
        return new Argument(targetAttr);
    }

    /*
     * 设置参数语句表达式描述
     * 通过描述字符串设置关联属性。注意：不要与attr混用
     * 格式: targetAttr=select(selectAttr:selectAttrTypeName)
     * @param exprDescribe 查询参数表达式描述
     * @return
     *
    @Override
    public Argument expr(String exprDescribe) {
        ArgumentExprDescribeParser ap = new ArgumentExprDescribeParser(exprDescribe, MapperAttrStatements.Argument.class);
        Map<String,String> argMap = ap.parse(SINGLE_FORMAT, SINGLE);
        return new Argument(argMap.get("targetAttr"))
                .collection(argMap.get("collectionType"))
                .used(argMap.get("selectAttr"))
                .usedType(argMap.get("selectAttrTypeName"));
    }*/

    /**
     * 设置参数语句表达式描述
     * 通过描述字符串设置关联属性。注意：不要与attr混用
     * 格式: targetAttr:collectionType=select(selectAttr:selectAttrTypeName)
     * @param exprDescribe 查询参数表达式描述
     * @return
     */
    public Argument expr(String exprDescribe) {
        // targetAttr:collectionType=select(selectAttr:selectAttrTypeName)
        Map<String,Object> argMap = parser.toMap(exprDescribe);
        return new Argument(argMap.get("targetAttr").toString())
                .collection(argMap.get("collectionType").toString())
                .used(argMap.get("selectAttr").toString())
                .usedType(argMap.get("selectAttrTypeName").toString());
    }

    public Set<MapperAttrExpression> getSelectMap() {
        return selectSet;
    }

    /**
     * 聚合属性语句构建器
     */
    public class Argument {
        /**
         * 要设置Vo哪个属性
         * 例: Vo.product
         */
        private String targetAttr;
        /**
         * 集合的类型
         */
        private String collectionType;
        /**
         * 使用参数的哪个属性的的值作为Select参数
         * 例: Vo.productId
         */
        private String selectAttr;
        /**
         * selectAttr的类型名称
         * 例: 基础类型: long, 类类型: java.lang.Long
         */
        private String selectAttrTypeName;

        public Argument() {
        }

        /**
         * 构造器的构造函数
         * 例: Vo.product
         *
         * @param targetAttr
         */
        public Argument(String targetAttr) {
            this.targetAttr = targetAttr;
            this.selectAttrTypeName = "long";
            this.collectionType = "bean";
        }

        /**
         * 设置Vo属性的集合类型.
         * @param targetCollectionType 类全名, 例： java.util.HashSet
         * @return
         */
        public Argument collection(String targetCollectionType){
            this.collectionType = targetCollectionType;
            return this;
        }
        /**
         * 设置查询参数的属性名称. 通常是对象的关联属性
         * 例: Vo.productId
         *
         * @param selectAttr 查询参数的属性名称
         * @return 返回构造器
         */
        public Argument used(String selectAttr){
            this.selectAttr = selectAttr;
            return this;
        }

        /**
         * 设置查询参数的类型名称. 通常是对象的关联属性
         * 例: 基础类型: long, 类类型: java.lang.Long
         *
         * @param selectAttrTypeName
         * @return
         */
        public Argument usedType(String selectAttrTypeName){
            this.selectAttrTypeName = selectAttrTypeName;
            return this;
        }

        /**
         * 设置查询参数的类型名称. 通常是对象的关联属性
         * 使用内置的类型枚举.只支持String和8种基础类型及其包装类
         * @param selectAttrType
         * @return
         */
        public Argument usedType(TypeEnum selectAttrType){
            this.selectAttrTypeName = selectAttrType.getType();
            return this;
        }
        public String getTargetAttr() {
            return targetAttr;
        }

        public String getSelectAttr() {
            return selectAttr;
        }

        public String getSelectAttrTypeName() {
            return selectAttrTypeName;
        }

        public String getCollectionType() {
            return collectionType;
        }

        /**
         * 设置目标对象属性值的查询函数
         * @param selectFun 查询函数
         * @return 聚合属性表达式
         * @param <R> 查询函数的返回值类型
         */
        public <R> MapperAttrStatements select(Function<String,R> selectFun) {
            MapperAttrExpression maq = new MapperAttrExpression(
                    this.targetAttr,
                    this.collectionType,
                    this.selectAttr,
                    this.selectAttrTypeName,
                    selectFun);
            getSelectMap().add(maq);
            return new MapperAttrStatements(getSelectMap());
        }

        public boolean isTargetAttrSet(){
            return null !=this.collectionType && this.collectionType.startsWith("java.util.");
        }

        @Override
        public String toString() {
            // targetAttr:collectionType=select(selectAttr:selectAttrTypeName)
            return String.format("%s:%s=select(%s:%s)",  targetAttr, collectionType, selectAttr, selectAttrTypeName);
        }
    }
}
